/*
 * Copyright 2024 Broadcom Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mab_include.h"
#include "mab_db.h"
#include "mab_client.h"
#include "mab_struct.h"

extern mabBlock_t *mabBlock;

extern void  mabWarmRestartTypeSet( BOOL  FALSE);

/*********************************************************************
 * @purpose  Handles events generated by NIM
 *
 * @param    intIfNum   @b{(input)) internal interface number
 * @param    intfEvent  @b{(input)) interface event
 * @param    correlator @b{(input)) Correlator for the event
 * @param    eventData  @b((input)) Additional event specific data
 *
 * @returns   SUCCESS  on a successful operation
 * @returns   FAILURE  for any error
 *
 * @comments
 *
 * @end
 *********************************************************************/
RC_t mabIntfChangeCallback(uint32 intIfNum, uint32 intfEvent, 
                              NIM_CORRELATOR_t correlator,
                              NIM_EVENT_SPECIFIC_DATA_t eventData)
{
  mabIntfChangeParms_t parms;

  parms.intfEvent = intfEvent;
  parms.nimCorrelator = correlator;

  return mabIssueCmd(mabIntfChange, intIfNum, &parms);
}

/*********************************************************************
 * @purpose  Handles startup notification from NIM
 *
 * @param    intIfNum   @b{(input)) internal interface number
 * @param    intfEvent  @b{(input)) interface event
 * @param    correlator @b{(input)) Correlator for the event
 *
 * @returns   SUCCESS  on a successful operation
 * @returns   FAILURE  for any error
 *
 * @comments
 *
 * @end
 *********************************************************************/
void mabIntfStartupCallback(NIM_STARTUP_PHASE_t startupPhase)
{
  /* hold NIM till MAB is ready */
  while (!(MAB_IS_READY))
  {
     LOGF( LOG_SEVERITY_ERROR,
        "Received an interface startup callback while MAB is not ready(startup phase=%d). waiting..",
        startupPhase);
    osapiSleep(1);
  }
  mabIssueCmd(mabIntfStartup,  NULL, &startupPhase);
  return;
}

/*********************************************************************
 * @purpose  Process NIM startup callback
 *
 * @param    startupPhase @b{(input)) CREATE or ACTIVATE
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments none
 *
 * @end
 *********************************************************************/
RC_t mabIhProcessIntfStartup(NIM_STARTUP_PHASE_t startup_phase)
{
  RC_t rc;
  uint32 intIfNum = 0;
  PORTEVENT_MASK_t nimEventMask;
  uint32 startTime, endTime;
   uchar8 ifName[ NIM_IF_ALIAS_SIZE + 1];


  MAB_EVENT_TRACE("%s:%d Received event %d from NIM and warm restart %d \n",
      __FUNCTION__,__LINE__,startup_phase,mabIsRestartTypeWarm());

  startTime = osapiTimeMillisecondsGet();
  switch (startup_phase)
  {
    case NIM_INTERFACE_CREATE_STARTUP:
      rc = nimFirstValidIntfNumber(&intIfNum);

      while (rc ==  SUCCESS)
      {
        if (mabIsValidIntf(intIfNum) ==  TRUE)
        {
          if (mabIntfCreate(intIfNum) !=  SUCCESS)
          {
            nimGetIntfName(intIfNum,  ALIASNAME, ifName);
             LOGF( LOG_SEVERITY_ERROR,
                "Error in creating interface %s.", ifName);
          }
        }
        rc = nimNextValidIntfNumber(intIfNum, &intIfNum);
      }

      /* Now ask NIM to send any future changes for these event types */
      memset(&nimEventMask, 0, sizeof(PORTEVENT_MASK_t));
      PORTEVENT_SETMASKBIT(nimEventMask,  CREATE);
      PORTEVENT_SETMASKBIT(nimEventMask,  DELETE);
      nimRegisterIntfEvents( MAB_COMPONENT_ID, nimEventMask);
      break;

    case NIM_INTERFACE_ACTIVATE_STARTUP:

 

      rc = mabIntfActivateStartup();


      /* Now ask NIM to send any future changes for these event types */
      memset(&nimEventMask, 0, sizeof(PORTEVENT_MASK_t));
      PORTEVENT_SETMASKBIT(nimEventMask,  CREATE);
      PORTEVENT_SETMASKBIT(nimEventMask,  DELETE);
      PORTEVENT_SETMASKBIT(nimEventMask,  ATTACH);
      PORTEVENT_SETMASKBIT(nimEventMask,  DETACH);
      PORTEVENT_SETMASKBIT(nimEventMask,  PORT_ENABLE);
      PORTEVENT_SETMASKBIT(nimEventMask,  PORT_DISABLE);
      PORTEVENT_SETMASKBIT(nimEventMask,  UP);
      PORTEVENT_SETMASKBIT(nimEventMask,  DOWN);
      PORTEVENT_SETMASKBIT(nimEventMask,  PROBE_SETUP);
      PORTEVENT_SETMASKBIT(nimEventMask,  PROBE_TEARDOWN);
      PORTEVENT_SETMASKBIT(nimEventMask,  AUTHMGR_ACQUIRE);
      nimRegisterIntfEvents( MAB_COMPONENT_ID, nimEventMask);

    (void) osapiTaskInitDone ( MAB_DB_TASK_SYNC);

      break;

    default:
      break;
  }

  endTime = osapiTimeMillisecondsGet();
  nimStartupEventDone( MAB_COMPONENT_ID);

  return  SUCCESS;
}

/*********************************************************************
 * @purpose  Process Link state changes
 *
 * @param    intIfNum   @b{(input)) internal interface number
 * @param    intIfEvent @b{(input)) interface event
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments none
 *
 * @end
 *********************************************************************/
RC_t mabIhProcessIntfChange(uint32 intIfNum, uint32 intfEvent, NIM_CORRELATOR_t correlator)
{
  uint32 linkState, adminState;
  RC_t rc =  SUCCESS;
  uint32 currAcquired, prevAcquired;
  NIM_EVENT_COMPLETE_INFO_t status;
  mabPortCfg_t *pCfg;
  uint32       lIntIfNum;
  mabLogicalPortInfo_t *logicalPortInfo;
   uchar8 ifName[ NIM_IF_ALIAS_SIZE + 1];

  status.intIfNum     = intIfNum;
  status.component    =  MAB_COMPONENT_ID;
  status.event        = intfEvent;
  status.correlator   = correlator;
  nimGetIntfName(intIfNum,  ALIASNAME, ifName);

  if (!(MAB_IS_READY))
  {
     LOGF( LOG_SEVERITY_INFO,
        "Received an interface change callback while MAB not ready to receive it.");
    MAB_EVENT_TRACE("%s:%d Nim Event %d received for %s when mab is not ready \n",
        __FUNCTION__,__LINE__,intfEvent,ifName);
    rc =  FAILURE;
    status.response.rc = rc;
    nimEventStatusCallback(status);
    return rc;
  }

  MAB_EVENT_TRACE("%s:%d Nim Event %d received for %s \n",
      __FUNCTION__,__LINE__,intfEvent,ifName);

  if (mabIsValidIntf(intIfNum) !=  TRUE)
  {
    rc =  SUCCESS;
    status.response.rc = rc;
    nimEventStatusCallback(status);
    return rc;
  }

  switch (intfEvent)
  {
    case  UP:
    case  PORT_ENABLE:
      mabBlock->mabPortInfo[intIfNum].portEnabled =  FALSE;
      if (mabIntfIsConfigurable(intIfNum, &pCfg) ==  TRUE)
      {
      if (nimGetIntfLinkState(intIfNum, &linkState) ==  SUCCESS)
      {
        if ( (nimGetIntfAdminState(intIfNum, &adminState) ==  SUCCESS) &&
            (adminState ==  ENABLE) &&
            (nimGetIntfLinkState(intIfNum, &linkState) ==  SUCCESS) &&
            (linkState ==  UP) )
        {
          mabBlock->mabPortInfo[intIfNum].portEnabled =  TRUE;
        }
        else
        {
          mabBlock->mabPortInfo[intIfNum].portEnabled =  FALSE;
        }
      }

        if (mabBlock->mabPortInfo[intIfNum].portEnabled ==  TRUE)
        {
          {
            /* Cleanup any existing clients on the port before reset */
            mabPortInfoCleanup(intIfNum);
            (void) mabPortReset(intIfNum);
            (void) mabCtlApplyPortConfigData(intIfNum);
          }
        }
      }
      break;

    case  PROBE_TEARDOWN:
      /* update aquired list */
      COMPONENT_ACQ_CLRMASKBIT(mabBlock->mabPortInfo[intIfNum].acquiredList,  PORT_MIRROR_COMPONENT_ID);


      /* check if currently acquired */
      COMPONENT_ACQ_NONZEROMASK(mabBlock->mabPortInfo[intIfNum].acquiredList, currAcquired);

      /* If the interface is not currently acquired by some component, cause
       * the acquisition to be reflected in the state of the spanning tree
       */
      if (currAcquired ==  FALSE)
      {
        if (nimGetIntfLinkState(intIfNum, &linkState) ==  SUCCESS)
        {
          if ( (nimGetIntfAdminState(intIfNum, &adminState) ==  SUCCESS) &&
              (adminState ==  ENABLE) &&
              (nimGetIntfLinkState(intIfNum, &linkState) ==  SUCCESS) &&
              (linkState ==  UP) )
          {
            mabBlock->mabPortInfo[intIfNum].portEnabled =  TRUE;
            /* Cleanup any existing clients on the port before reset */
            mabPortInfoCleanup(intIfNum);
            (void) mabPortReset(intIfNum);
            (void) mabCtlApplyPortConfigData(intIfNum);
          }
        }
      }
      break;

    case  DOWN:
    case  PORT_DISABLE:

      if (mabIntfIsConfigurable(intIfNum, &pCfg) ==  TRUE )
      {
        lIntIfNum = MAB_LOGICAL_PORT_ITERATE;
        while ((logicalPortInfo=mabLogicalPortInfoGetNextNode(intIfNum,&lIntIfNum))!=  NULLPTR)
        {
          if (0 == logicalPortInfo->key.keyNum)
            continue;

          if (mabBlock->mabPortInfo[intIfNum].portControlMode ==  AUTHMGR_PORT_AUTO)
          {
            mabPortInfoCleanup(intIfNum);
          }
        }
      }
      break;

    case  PROBE_SETUP:
      /* check if previously acquired */
      COMPONENT_ACQ_NONZEROMASK(mabBlock->mabPortInfo[intIfNum].acquiredList, prevAcquired);
      COMPONENT_ACQ_SETMASKBIT(mabBlock->mabPortInfo[intIfNum].acquiredList,  PORT_MIRROR_COMPONENT_ID);

      /* If the interface was not previously acquired by some component, cause
       * the acquisition to be reflected in the state of the spanning tree
       */
      if (prevAcquired ==  FALSE)
      {
        if (mabBlock->mabPortInfo[intIfNum].portControlMode !=  AUTHMGR_PORT_FORCE_AUTHORIZED)
        {
          /* reset all the clients associated with the port if port control mode is Macbased */
          if (mabBlock->mabPortInfo[intIfNum].portControlMode ==  AUTHMGR_PORT_AUTO)
          {
            /* remove clients from any associated vlans*/
            mabPortInfoCleanup(intIfNum);
            /* reset all the clients associated with the port */
            (void) mabPortReset(intIfNum);
            (void) mabCtlApplyPortConfigData(intIfNum);
          }
        }
      }
      break;

    case  CREATE:
      rc = mabIntfCreate(intIfNum);
      break;

    case  ATTACH:
      if (mabIntfIsConfigurable(intIfNum, &pCfg) ==  TRUE)
      {
        (void) mabPortReset(intIfNum);
      }
      else
      {
        rc =  FAILURE;
      }
      break;

    case  DETACH:
      rc = mabIntfDetach(intIfNum);
      break;

    case  DELETE:
      rc = mabIntfDelete(intIfNum);
      break;

    case  AUTHMGR_ACQUIRE:
      rc =  SUCCESS;
      break;

    default:
      break;
  }

  status.response.rc = rc;
  nimEventStatusCallback(status);

  return rc;
}

/*********************************************************************
 * @purpose  bring up the individual interface
 *
 * @param    warmRestart  BOOL true if restart type is warm
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments Handle the events ATTACH, UP, Acquire etc
 *
 * @end
 *********************************************************************/
RC_t mabIntfStart(uint32 intIfNum,  BOOL warmRestart)
{
  mabPortCfg_t *pCfg;
  RC_t rc =  FAILURE;
  uint32 portLinkState;
   COMPONENT_IDS_t acqCompId =  FIRST_COMPONENT_ID;
  uint32 adminState, count = 0;
   BOOL probePort, restore =  FALSE, skipFlag =  FALSE;
   BOOL needCleanup =  TRUE;

  do
  {
    if (mabIntfIsConfigurable(intIfNum, &pCfg) !=  TRUE)
    {  
      return rc;
    }


    /* Link state */
    rc = nimGetIntfLinkState(intIfNum, &portLinkState);
    if (rc !=  SUCCESS)
    {
      break;
    }
    rc = nimGetIntfAdminState(intIfNum, &adminState);
    if (rc !=  SUCCESS)
    {
      break;
    }

    if (( TRUE != warmRestart) ||
        (0 == count))
    {
      (void) mabPortReset (intIfNum);
      rc = mabCtlApplyPortConfigData (intIfNum);
    }
    else
    {
      (void) mabPortReset (intIfNum);

      mabPortInfoInitialize (intIfNum,  TRUE);
      skipFlag =  TRUE;
    }


    if (probePort)
    {
      acqCompId =  PORT_MIRROR_COMPONENT_ID;
    }

    if (acqCompId !=  FIRST_COMPONENT_ID)
    {
      COMPONENT_ACQ_SETMASKBIT(mabBlock->mabPortInfo[intIfNum].acquiredList, acqCompId);
    }
    else
    {
      if (portLinkState ==  UP)
      {
          if ((restore) && (warmRestart ==  TRUE) && (skipFlag ==  TRUE) &&
               ((mabBlock->mabPortInfo[intIfNum].portControlMode ==   AUTHMGR_PORT_AUTO)))
          {
            count = 0;
            if (0 != count)
            {
              needCleanup =  FALSE;
            }
          }
      } /* end if link state is up*/
    }/* end intf not acquired*/
  }
  while ( FALSE);
  /* Acquire /Release*/
  return rc;

}

/*********************************************************************
 * @purpose  Process the activate startup callback from NIM
 *
 * @param
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments none
 *
 * @end
 *********************************************************************/
RC_t mabIntfActivateStartup()
{
 RC_t rc;
  uint32 intIfNum = 0;

  rc = nimFirstValidIntfNumber(&intIfNum);

  while (rc ==  SUCCESS)
  {
    if (mabIsValidIntf(intIfNum) ==  TRUE)
    {
      mabIntfStart(intIfNum, mabIsRestartTypeWarm());
    }
    rc = nimNextValidIntfNumber(intIfNum, &intIfNum);
  }

  mabBlock->mabSwitchoverInProgress =  FALSE;
  mabWarmRestartTypeSet(FALSE);

  return  SUCCESS;
}

/*********************************************************************
 * @purpose  Obtain a pointer to the specified interface configuration data
 *           for this interface
 *
 * @param    intIfNum @b{(input)} Internal Interface Number
 * @param    **pCfg   @b{(output)}  Ptr  to mab port config structure
 *                           or  NULL if not needed
 *
 * @returns   TRUE
 * @returns   FALSE
 *
 *
 * @comments Facilitates pre-configuration, as it checks if the NIM
 *           interface exists and whether the component is in a state to
 *           be configured (regardless of whether the component is enabled
 *           or not).
 *
 * @comments The caller can set the pCfg parm to  NULL if it does not
 *           want the value output from this function.
 *
 * @end
 *********************************************************************/
 BOOL mabIntfIsConfigurable(uint32 intIfNum, mabPortCfg_t **pCfg)
{
  nimConfigID_t configId;
   uchar8 ifName[ NIM_IF_ALIAS_SIZE + 1];
  nimGetIntfName(intIfNum,  ALIASNAME, ifName);

  if (!(MAB_IS_READY))
    return  FALSE;

  /* Check boundary conditions */
  if (intIfNum <= 0 || intIfNum >=  MAB_INTF_MAX_COUNT)
    return  FALSE;


  /* verify that the configId in the config data table entry matches the configId that NIM maps to
   ** the intIfNum we are considering
   */
  if (nimConfigIdGet(intIfNum, &configId) ==  SUCCESS)
  {
    if (NIM_CONFIG_ID_IS_EQUAL(&configId, &(mabBlock->mabCfg->mabPortCfg[intIfNum].configId)) ==  TRUE)
    {
      *pCfg = &mabBlock->mabCfg->mabPortCfg[intIfNum];
      return  TRUE;
    }
  }

  /* if we get here, either we have a table management error between mabCfg and mabMapTbl or
   ** there is synchronization issue between NIM and components w.r.t. interface creation/deletion
   */
   LOGF( LOG_SEVERITY_INFO,
      "Error accessing MAB config data for interface %s in MAB interface configuration.\n", ifName);
  return  FALSE;
}

/*********************************************************************
 *
 * @purpose  To process the Callback for  CREATE
 *
 * @param    uint32  intIfNum  internal interface number
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments none
 *
 * @end
 *
 *********************************************************************/
RC_t mabIntfCreate(uint32 intIfNum)
{
  nimConfigID_t configId;
  mabPortCfg_t *pCfg;

  if (mabIsValidIntf(intIfNum) !=  TRUE)
    return  FAILURE;

  if (nimConfigIdGet(intIfNum, &configId) !=  SUCCESS)
    return  FAILURE;

  pCfg = &mabBlock->mabCfg->mabPortCfg[intIfNum];

  if (!(NIM_CONFIG_ID_IS_EQUAL(&configId, &pCfg->configId)))
  {
    NIM_CONFIG_ID_COPY(&pCfg->configId, &configId);
    /* If an interface configuration entry is not already assigned to the interface, assign one */
    /* Update the configuration structure with the config id */
    mabBuildDefaultIntfConfigData(&configId, pCfg);
  }

  (void)mabCtlPortStatsClear(intIfNum);

  return  SUCCESS;
}

/*********************************************************************
 *
 * @purpose  To process the Callback for  DETACH
 *
 * @param    uint32  intIfNum  internal interface number
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments none
 *
 * @end
 *
 *********************************************************************/
RC_t mabIntfDetach(uint32 intIfNum)
{
  mabPortCfg_t *pCfg;

  if (mabIntfIsConfigurable(intIfNum, &pCfg) ==  TRUE)
  {
    mabPortInfoCleanup(intIfNum);
    (void)mabCtlApplyPortConfigData(intIfNum);
  }

  return  SUCCESS;
}

/*********************************************************************
 *
 * @purpose  To process the Callback for  DELETE
 *
 * @param    uint32  intIfNum  internal interface number
 *
 * @returns   SUCCESS
 * @returns   FAILURE
 *
 * @comments none
 *
 * @end
 *
 *********************************************************************/
RC_t mabIntfDelete(uint32 intIfNum)
{
  mabPortCfg_t *pCfg;

  if (mabIntfIsConfigurable(intIfNum, &pCfg) ==  TRUE)
  {
    mabPortInfoCleanup(intIfNum);

    memset((void *)&pCfg->configId, 0, sizeof(nimConfigID_t));
    memset((void *)&mabBlock->mabMapTbl[intIfNum], 0, sizeof(uint32));
    memset((void *)&mabBlock->mabPortInfo[intIfNum], 0, sizeof(mabPortInfo_t));
    memset((void *)&mabBlock->mabPortStats[intIfNum], 0, sizeof(mabPortStats_t));
  }

  return  SUCCESS;
}
